#include "msgbus.hpp"

#include <stdlib.h>
#include <string.h>

#include "msgbus.h"

namespace msgbus {
    MsgHandler::MsgHandler()
    :auto_free_(false)
    {

    }
    MsgHandler::MsgHandler(bool auto_free)
    :auto_free_(auto_free)
    {

    }
    MsgHandler::~MsgHandler() {

    }

    static void cb_handle(const char *topic, const char *data, int data_len, void *args)
    {
        MsgHandler *handler = static_cast<MsgHandler*>(args);
        if(handler) {
            handler->handle(topic, data, data_len);
        }
    }

    static char* cb_call(const char *topic, const char *method, const char *data, int data_len, void *args, int *return_len)
    {
        MsgHandler *handler = static_cast<MsgHandler*>(args);
        if(handler) {
            std::string return_data;
            bool ret = handler->call(topic, method, data, data_len, &return_data);
            if(ret) {
                char *buff = nullptr;
                if(!return_data.empty()) {
                    buff = (char*)malloc(return_data.size());
                    memcpy(buff, return_data.data(), return_data.size());
                }
                if(return_len) {
                    *return_len = return_data.size();
                }
                return buff;
            }
        }
        if(return_len) {
            *return_len = 0;
        }
        return nullptr;
    }

    static void cb_destroy(void *args)
    {
        MsgHandler *handler = (MsgHandler*)(args);
        if(handler && handler->need_free()) {
            delete handler;
        }
    }

    MsgBus::MsgBus() {
        msgbus_ = ::get_bus();
    }

    MsgBus* MsgBus::inst() {
        static MsgBus msgbus;
        return &msgbus;
    }

//    void MsgBus::enable_log(int level)
//    {
//        ::enable_log(level);
//    }

    // @return subscribe token
    std::string MsgBus::subscribe(const char *topic, MsgHandler *handler) {
        MessageBusCallback cb {cb_handle, cb_call, cb_destroy};
        char *token = ::subscribe((struct MessageBus*)msgbus_, topic, cb, handler);
        if(token) {
            std::string tk = std::string(token, strlen(token));
            free(token);
            return tk;
        }
        return "";
    }

    void MsgBus::unsubscribe(const char *token) {
        ::unsubscribe((struct MessageBus*)msgbus_, token);
    }

    void MsgBus::unsubscribe_topic(const char *topic) {
        ::unsubscribe_topic((struct MessageBus*)msgbus_, topic);
    }

    void MsgBus::publish(const char *topic, const char *data, int data_len, int to_in_ms) {
        ::publish((struct MessageBus*)msgbus_, topic, data, data_len, to_in_ms);
    }

    bool MsgBus::call(const char *topic, const char *method, const char *args, int args_len, std::string *out_data, int to_in_ms) {
        int data_len = 0;
        char *data = nullptr;
        if(::call((struct MessageBus*)msgbus_, topic, method, args, args_len, &data, &data_len, to_in_ms) == 0) {
            if(data && data_len >0) {
                if(out_data) {
                    out_data->assign(data, data_len);
                }
                free(data);
            }
            return true;
        }
        return false;
    }
}
